package com.ajx.blog.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Reference(url = "dubbo://localhost:20880")
    private UserDetailsService userDetailsService;

    @Bean
    PasswordEncoder passwordEncoder() {
        // 不对密码进行加密(在springsecurity5中默认需要进行密码加密)
        return NoOpPasswordEncoder.getInstance();
    }

    // 角色继承
    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_CHECK_EMP");
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_CHECK_MANAGE");
        hierarchy.setHierarchy("ROLE_ADMIN > ROLE_COURSE");
        hierarchy.setHierarchy("ROLE_CHECK_MANAGE > ROLE_CHECK_EMP");
        return hierarchy;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 用户
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        Map<String, Object> returnMap = new HashMap<>();
        // 定制请求的授权规则,
        http.authorizeRequests()
                // 匹配许可,首页所有人可以访问
                .antMatchers("/", "/css/**", "/editor/**", "/js/**", "/src/**", "/lib/**", "/upload/**", "/images/**",
                        "/login.html",
                        "/index.html",
                        "/blog/task/**")
                .permitAll()
                // 匹配访问规则
                .antMatchers("/blog/admin/**").hasAnyRole("ADMIN")
                .antMatchers("/blog/userManage/**").hasAnyRole("ADMIN")
                .antMatchers("/blog/checkEmp/**").hasAnyRole("CHECK_EMP", "ADMIN")
                .antMatchers("/blog/course/root/**").hasAnyRole("COURSE", "ADMIN")
                .antMatchers("/**").authenticated();

        // 开启自动配置的登录功能,必须设定自定义登录页面和登录的请求路径
        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                // .usernameParameter("aname") // 指定自定义的登录页面用户名
                // .passwordParameter("apass") // 指定自定义的登录页面密码
                .successHandler(new SimpleUrlAuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                            HttpServletResponse httpServletResponse, Authentication authentication)
                            throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        ServletOutputStream out = httpServletResponse.getOutputStream();
                        ObjectMapper objectMapper = new ObjectMapper();
                        returnMap.put("code", 0);
                        returnMap.put("data", null);
                        returnMap.put("msg", "登录成功");
                        logger.info(
                                "[SUCCESS]:调用了登陆方法,登陆成功-"
                                        + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
                        objectMapper.writeValue(out, returnMap);
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new SimpleUrlAuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                            HttpServletResponse httpServletResponse, AuthenticationException e)
                            throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        ServletOutputStream out = httpServletResponse.getOutputStream();
                        ObjectMapper objectMapper = new ObjectMapper();
                        returnMap.put("code", -1);
                        returnMap.put("data", null);
                        returnMap.put("msg", "用户名或密码错误");
                        logger.info("[ERROR]:调用了登陆方法,登陆失败");
                        objectMapper.writeValue(out, returnMap);
                        out.flush();
                        out.close();
                    }
                });

        // 注销登录
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login.html")
                .clearAuthentication(true)
                .invalidateHttpSession(true);

        // 关闭csrf
        http.csrf().disable();
        // 可以使用ifream框架
        http.headers().frameOptions().sameOrigin();
        // 自定义无权限页面(无权限跳转到自定义403页面)
        http.exceptionHandling().accessDeniedPage("/error.html");

    }

}
